/*global analogue*/
/*jslint indent: 4*/

/*Analogue.Forms 1.11.12, Copyright (c) 2011 Analogue Web Design LLC, all rights reserved.*/
analogue.extend((function (document, window, $) {
    'use strict';

    var build = new Date(1323918453351),

        filter = function (tags) {
            var regex = new RegExp('^' + tags + '$', 'i');
            return function (element) {
                return regex.test(element.tagName);
            };
        },

        format = function (string) {
            var args = [].slice.call(arguments, 1);
            return string.replace(/\{(\d+)\}/g, function (match, i) {
                return typeof args[i] !== 'undefined' ? args[i] : match;
            });
        },

        isEmpty = function (value) {
            return value === null || value.length === 0 || /^\s+$/.test(value);
        },

        messages = {
            accept: 'You must {0}.',
            date: '{0} does not appear to be a valid date.',
            email: '{0} does not appear to be a valid email address.',
            group: 'Please select an option in the {0} group.',
            least: 'Please select at least {0} options in the {1} group.',
            list: 'Please select an option from the {0} list.',
            max: 'Please enter a value less than or equal to {0}.',
            min: 'Please enter a value greater than or equal to {0}.',
            most: 'Please select no more than {0} options in the {1} group.',
            number: '{0} does not appear to be a valid number.',
            tel: '{0} does not appear to be a valid telephone number.',
            required: 'The {0} field is required.',
            url: '{0} does not appear to be a valid URL.'
        },

        rules = (function () {
            var date = function (value) {
                    var day,
                        days,
                        match = /^(\d{1,2})[\s\.\/\-](\d{1,2})[\s\.\/\-](\d{4})$/.exec(value),
                        month,
                        year;
                    if (!match) {
                        return false;
                    }
                    month = parseInt(match[1], 10);
                    day = parseInt(match[2], 10);
                    year = parseInt(match[3], 10);
                    if (month < 1 || month > 12 || year < 1900 || year > 2100) {
                        return false;
                    }
                    switch (month) {
                    case 2:
                        days = ((year % 4) === 0) ? 29 : 28;
                        break;
                    case 4:
                    case 6:
                    case 9:
                    case 11:
                        days = 30;
                        break;
                    default:
                        days = 31;
                    }
                    return (day >= 1 && day <= days);
                },
                group = function (element) {
                    var children,
                        count = 0,
                        i,
                        ii,
                        valid = null;
                    if (element.tagName === 'INPUT') {
                        switch (element.get('type')) {
                        case 'checkbox':
                        case 'radio':
                            valid = !!element.get('checked');
                            break;
                        default:
                            valid = !isEmpty(element.get('value'));
                        }
                    } else {
                        valid = !isEmpty(element.get('value'));
                    }
                    if (valid) {
                        count += 1;
                    }
                    children = element.children();
                    for (i = 0, ii = children.length; i < ii; i += 1) {
                        if (children[i].nodeType === 1) {
                            count += group($(children[i]));
                        }
                    }
                    return count;
                };
            return {
                date: {
                    filter: filter('input'),
                    validate: function (element) {
                        var value = element.get('value');
                        if (value && !date(value)) {
                            return format(messages.date, value);
                        }
                        return true;
                    }
                },
                email: {
                    filter: filter('input'),
                    validate: function (element) {
                        var value = element.get('value');
                        if (value && !/^([\w\-]+(?:\.[\w\-]+)*)@((?:[\w\-]+\.)*\w[\w\-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/.test(value)) {
                            return format(messages.email, value);
                        }
                        return true;
                    }
                },
                number: {
                    filter: filter('input'),
                    validate: function (element) {
                        var match,
                            title = element.get('title'),
                            value = element.get('value');
                        if (value && !/^[\d\.]+$/.test(element.get('value'))) {
                            return format(messages.number, title);
                        }
                        if ((match = /max=(\d+)/.exec(element[0].className)) !== null) {
                            if (parseInt(value, 10) > parseInt(match[1], 10)) {
                                return format(messages.max, match[1]);
                            }
                        }
                        if ((match = /min=(\d+)/.exec(element[0].className)) !== null) {
                            if (parseInt(value, 10) < parseInt(match[1], 10)) {
                                return format(messages.min, match[1]);
                            }
                        }
                        return true;
                    }
                },
                tel: {
                    filter: filter('input'),
                    validate: function (element) {
                        var value = element.get('value');
                        if (value && !/^([1]?[\s\.\-]?\(?[2-9]\d{2}\)?)?[\s\.\-]?(\d{3}[\s\.\-]?\d{4})$/.test(value)) {
                            return format(messages.tel, value);
                        }
                        return true;
                    }
                },
                required: {
                    filter: filter('fieldset|input|select|textarea'),
                    validate: function (element) {
                        var count,
                            match,
                            title = element.get('title'),
                            value = element.get('value');
                        switch (element.tagName) {
                        case 'FIELDSET':
                            count = group(element);
                            if (count === 0) {
                                return format(messages.group, title);
                            }
                            if ((match = /max=(\d+)/.exec(element[0].className)) !== null) {
                                if (count > parseInt(match[1], 10)) {
                                    return format(messages.most, match[1], title);
                                }
                            }
                            if ((match = /min=(\d+)/.exec(element[0].className)) !== null) {
                                if (count < parseInt(match[1], 10)) {
                                    return format(messages.least, match[1], title);
                                }
                            }
                            break;
                        case 'INPUT':
                        case 'TEXTAREA':
                            switch (element.get('type')) {
                            case 'checkbox':
                            case 'radio':
                                if (!element.get('checked')) {
                                    return format(messages.accept, title);
                                }
                                break;
                            default:
                                if ((element.hasClass('placeholder') && value === element.get('title')) || isEmpty(value)) {
                                    return format(messages.required, title);
                                }
                            }
                            break;
                        case 'SELECT':
                            if (element[0].selectedIndex === 0) {
                                return format(messages.list, title);
                            }
                            break;
                        }
                        return true;
                    }
                },
                url: {
                    filter: filter('input'),
                    validate: function (element) {
                        var value = element.get('value');
                        if (value && !/^(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?\^=%&amp;:\/~\+#]*[\w\-\@?\^=%&amp;\/~\+#])?/.test(value)) {
                            return format(messages.url, value);
                        }
                        return true;
                    }
                }
            };
        }()),

        validate = function (event) {
            var elements = $('fieldset, input, select, textarea', this),
                error,
                errors = [],
                form = $(this),
                i,
                ii,
                message,
                modal = form.hasClass('confirm') || form.hasClass('alert'),
                stop = form.hasClass('stop');
            elements.forEach(function () {
                var e,
                    element = $(this),
                    test,
                    span,
                    valid;
                element.removeClass('invalid');
                if (!modal) {
                    if (element.nextSibling() && element.nextSibling().tagName === 'SPAN') {
                        element.nextSibling().remove();
                    } else if (element.lastChild() && element.lastChild().tagName === 'SPAN') {
                        element.lastChild().remove();
                    }
                }
                for (test in rules) {
                    if (rules.hasOwnProperty(test) && element.hasClass(test) && rules[test].filter(element)) {
                        valid = rules[test].validate(element);
                        if (valid !== true) {
                            e = errors.push(valid);
                            element.addClass('invalid');
                            if (!error) {
                                error = element[0];
                            }
                            if (!modal) {
                                span = $('<span/>').set({
                                    'class': 'error',
                                    text: errors[e - 1]
                                });
                                if (element.tagName === 'FIELDSET') {
                                    span.appendTo(element);
                                } else {
                                    span.insertAfter(element);
                                }
                                if (stop) {
                                    return false;
                                }
                            }
                            break;
                        }
                    }
                }
            });
            if (errors.length === 0) {
                return true;
            }
            if (modal) {
                message = 'The following form ' + (errors.length > 1 ? 'fields were' : 'field is') + ' incomplete or incorrect:\n\n';
                for (i = 0, ii = errors.length; i < ii; i += 1) {
                    message += String(i + 1) + '. ' + errors[i] + '\n';
                }
                if (form.hasClass('confirm') && window.confirm(message + '\nYour form has not been submitted.\nClick \"OK\" to continue submitting the form; or\nClick \"Cancel\" to check the information that you provided and try again.')) {
                    return true;
                }
                if (form.hasClass('alert')) {
                    window.alert(message + '\nYour form has not been submitted.\nPlease check the information that you provided and submit the form again.');
                }
            }
            if (error && error.focus) {
                error.focus();
            }
            event.preventDefault();
            return false;
        },

        versioning = [1, String(build.getFullYear()).slice(2), String((build.getMonth() + 1) / 100).slice(2)];

    $(document).ready(function () {
        var placeholder = function () {
                if (this.value === '') {
                    this.value = this.title;
                } else if (this.value === this.title) {
                    this.value = '';
                }
            };
        $('form').forEach(function () {
            var form = $(this);
            form.addEvent('submit', validate);
            $('input, select, textarea', form).forEach(function () {
                var element = $(this);
                if (element.hasClass('placeholder')) {
                    element.addEvent('blur focus', placeholder);
                }
                if (element.hasClass('autofocus') && element[0].focus) {
                    element[0].focus();
                }
            });
        });
    });

    return {
        init: function () {
            $.forms = {
                extend: function (validation) {
                    var i,
                        rule;
                    for (i in validation) {
                        if (validation.hasOwnProperty(i)) {
                            rule = validation[i];
                            rule.filter = typeof rule.filter === 'string' ? filter(rule.filter) : filter('input');
                            rules[i] = rule;
                        }
                    }
                },
                version: versioning.join('.') + ' (' + parseInt(build.valueOf() / 1E3, 10) + ')'
            };
            return this;
        }
    };
}(this.document, this, analogue)));